
/*
author :shinion

date :2019-10-12

    1.通过传入的数据，筛选并生成合法的gps轨迹点


*/


var InsertGps = {};
InsertGps.debug = true;

//////////////////////////////////////////////////////////变量

InsertGps.msgDivId = "";


InsertGps.data = {};//自有数据
InsertGps.data.pointData = {
    time:"",
    timeLong:0,
    longitude:0,
    latitude:0
};

InsertGps.cache = {};//缓存数据
InsertGps.cache.start = InsertGps.data.pointData;//起点数据，{time,longitude,latitude}
InsertGps.cache.end = InsertGps.data.pointData;
InsertGps.cache.routList = [];//百度生成的路径列表，元素为经纬度对象
InsertGps.cache.statusData = {
    plateNo:"",
    simNo:"",
    plateColor:"",
    mileage:0,
    totalMile:0,
    
};//生成GPS轨迹点所需要的状态数据，车牌号，sim卡号，车牌颜色，起始里程
InsertGps.cache.waringMsg = "";//警告信息，有警告信息仍然可以插入数据
InsertGps.cache.errorMsg = "";//错误信息，有错误信息则不可以插入数据
InsertGps.cache.gpsList = [];//生成的gps轨迹列表
InsertGps.cache.debugMsg = [];//调试信息保存


InsertGps.config = {};//设置数据
InsertGps.config.maxSpeed = 80;//(km/h)
InsertGps.config.FastSpeed = 160;//漂移速度标准，(km/h)

InsertGps.config.noTirdFlg = true;//是否禁止疲劳驾驶，默认为true，表明禁止疲劳驾驶，自动进行时间调整
InsertGps.config.tirdValue = 14100;//疲劳驾驶最长延续时间，单位秒，14100为3小时55分钟
InsertGps.config.restValue = 22 * 60;//疲劳驾驶后的休息时间，单位秒

InsertGps.config.noDriveFlg = true;//是否禁止2点到5点间行车，默认为true
InsertGps.config.noDriveStartHour = 2;//禁止驾驶的开始小时，为2点，即02:00:00
InsertGps.config.noDriveEndHour = 5;//禁止驾驶的结束小时，为5点，即05:00:00

InsertGps.config.timeOffset = 3;//时间点偏移量，3表示前后三秒
InsertGps.config.onePointTime = 30;//每个数据点的间隔时间,单位为秒

InsertGps.calCache = {};//计算所需缓存


//////////////////////////////////////////////////////////初始化相关
//校验初始化数据是否完整
InsertGps.checkInitDatas = function(){
    this.checkStartAndEndData();//校验起点终点数据
    this.checkStatusData();//校验状态数据
    this.checkRoutList();//校验路径数据
}

//校验起点终点数据
InsertGps.checkStartAndEndData = function () {
    //校验起点数据
    let cache = this.cache;
    if (!DataUtil.isDefined(cache.start.time) && cache.start.time != "") {
    } else {
        cache.errorMsg += "起点时间数据缺失!";
    }
    if (!DataUtil.isDefined(cache.start.longitude) && cache.start.longitude > 0) {
    } else {
        cache.errorMsg += "起点经度数据缺失!";
    }
    if (!DataUtil.isDefined(cache.start.latitude) && cache.start.latitude > 0) {
    } else {
        cache.errorMsg += "起点纬度数据缺失!";
    }

    //终点
    if (!DataUtil.isDefined(cache.end.time) && cache.end.time != "") {
    } else {
        cache.errorMsg += "终点时间数据缺失!";
    }
    if (!DataUtil.isDefined(cache.end.longitude) && cache.end.longitude > 0) {
    } else {
        cache.errorMsg += "终点经度数据缺失!";
    }
    if (!DataUtil.isDefined(cache.end.latitude) && cache.end.latitude > 0) {
    } else {
        cache.errorMsg += "终点纬度数据缺失!";
    }

}

//校验状态数据信息
InsertGps.checkStatusData = function(){
    let cache = this.cache;
    if (!DataUtil.isDefined(cache.statusData.plateNo) && cache.statusData.plateNo != "") {
    } else {
        cache.errorMsg += "状态数据缺失!";
    }
    if (!DataUtil.isDefined(cache.statusData.simNo) && cache.statusData.simNo != "") {
    } else {
        cache.errorMsg += "simNo数据缺失!";
    }
    if (!DataUtil.isDefined(cache.statusData.plateColor) && cache.statusData.plateColor != "") {
    } else {
        cache.errorMsg += "车牌颜色数据缺失!";
    }
    if (!DataUtil.isDefined(cache.statusData.mileage) && cache.statusData.mileage > 0) {
    } else {
        cache.errorMsg += "起始里程数据缺失!";
    }
}

//校验路径列表
InsertGps.checkRoutList = function(){
    if(this.cache.routList.length <= 0){
        this.cache.errorMsg += "规划路径数据缺失！";
    }
}




//////////////////////////////////////////////////////////生成GPS轨迹数据

//初始化数据缓存
InsertGps.initDataCache = function () {
    this.cache.start = InsertGps.data.pointData;//起点数据，{time,longitude,latitude}
    this.cache.end = InsertGps.data.pointData;
    this.cache.routList = [];//百度生成的路径列表，元素为经纬度对象
    this.cache.statusData = {
        plateNo: "",
        simNo: "",
        plateColor: "",
        mileage: 0,
        totalMile: 0,
    };//生成GPS轨迹点所需要的状态数据，车牌号，sim卡号，车牌颜色，起始里程
    this.cache.waringMsg = "";//警告信息，有警告信息仍然可以插入数据
    this.cache.errorMsg = "";//错误信息，有错误信息则不可以插入数据
    this.cache.gpsList = [];//生成的gps轨迹列表
}


//初始化计算缓存
InsertGps.initCalCache = function(){
    InsertGps.calCache = {
        timeIndex:0,//时间索引，当前需要获取第几个时间索引
        routIndex:-1,//路径索引，当前所以已经被获取过或者筛选掉，所以默认从-1 开始，开始时即需要获取-1 + 1 即第0个
        proTirdTimeValLong:0,//上一个疲劳驾驶时间点，
        proTimeValLong:0,//上一个时间点，即获取下一个时间点以上一时间点为基准，本时间点不带偏移量，以毫秒为单位，长整型
        endTimeValueLong:0,//最后一个时间点，即终点
        speed :0,//保存平均速度值
        topSpeed:0,//定义当前轨迹点所需要的最低速度
        timeValue:0,//耗时，用于显示到页面上
    };
    this.cache.waringMsg = "";
    this.cache.errorMsg = "";
    this.cache.gpsList = [];//生成的gps轨迹列表
}



/*
思路
    0.初始化计算缓存信息
    1.计算平均速度和需要的时间点数量，是否超过4小时，是否有两点到5点间时间
    2.获取第一个轨迹点，如果获取第一个点没有错误，则继续获取下一个轨迹点，直到最后一个都正确，获取标准以路径点获取完为准
            如果错误，则停止获取，返回错误信息


*/


//获取GPS轨迹数据
InsertGps.getGpsListData = function(){
    let that = this;
    try {
        this.initCalCache();//初始化计算缓存
        this.calBasicData();

        if(this.cache.errorMsg != ""){
            // DataUtil.showErrorMsg(this.cache.errorMsg);
        }else{
            //开始计算,开始获取下一个gps数据点
            this.outDebug("开始进行计算.....");
            try {
                InsertGps.getNextTimeGpsData();
            } catch (error) {
                this.cache.errorMsg += "获取GPS轨迹点数据失败：" + error;
            }

            //获取完成，判断当前轨迹列表是否获取完成，如果没有获取完成，则说明时间不够，限速太低，调高限速，如果完成，则成功
            if(this.calCache.routIndex >= this.cache.routList.length -1){
                if(this.cache.waringMsg == "" && this.cache.errorMsg == ""){
                    this.showInfo("计算完成")
                }else{
                    this.showInfo(this.cache.errorMsg + this.cache.waringMsg);
                }
            }else{
                //轨迹没有获取完成，则是时间不够
                this.cache.errorMsg += "最高限速太低，无法完成全部轨迹，请调整最高限速!";
            }
            this.outDebug("警报信息：" ,this.cache.waringMsg);
            this.outDebug("错误信息：" , this.cache.errorMsg);
            // this.outDebug("调试信息",this.cache.debugMsg);
            this.outDebug("轨迹点数据数量："+ this.cache.routList.length);
            this.outDebug("轨迹数据：" , this.cache.gpsList);
        }
    } catch (error) {
        this.cache.errorMsg += "获取GPS数据轨迹点失败：" + error;
    }
     
}

//计算基本数据，判断平均速度是否合法
InsertGps.calBasicData = function(){
    //计算平均速度
    try {
        let start = new Date(this.cache.start.time).getTime();
        let end = new Date(this.cache.end.time).getTime();
        if(start < end){
            this.calCache.proTirdTimeValLong = start;//上一疲劳驾驶时间点为起点
            this.calCache.proTimeValLong = start;   //上一时间点为起点
            this.calCache.endTimeValueLong = end;   //终点时间点
            
            this.cache.start.timeLong = start;//起点值
            this.cache.end.timeLong = end;//终点值

            timeValue = parseInt((end - start - this.config.onePointTime * 1000)/1000);
            this.calCache.timeValue = timeValue;
            if(timeValue > this.config.onePointTime){

                //根据疲劳驾驶和时间范围获取时间值


                //校验车速
                let speed = this.cache.statusData.totalMile * 3.6 /timeValue;
                this.calCache.speed = speed;
                if(speed > this.config.maxSpeed){//平均车速大于允许最大车速
                    if(speed >= this.config.FastSpeed){
                        this.cache.waringMsg += "平均车速为" + speed.toFixed(1) + "(km/h)，大于" + this.config.FastSpeed + "(km/h)";
                    }else{
                        this.cache.errorMsg += "平均车速为" + speed.toFixed(1) + "(km/h)，大于" + this.config.maxSpeed + "(km/h)！";
                    }
                }
            }else{
                this.cache.errorMsg += "两点之间时间差小于"+ this.config.onePointTime + "秒！" ;
            }
        }else{
            this.cache.errorMsg += "开始时间大于等于结束时间！";
        }
    } catch (error) {
        this.cache.errorMsg += "计算基本信息失败：" + error;
    }
}


//获取下一个时间的GPS数据点
InsertGps.getNextTimeGpsData = function(){
    try {
        let point = this.getNextTimeValue();//获取到下一个时间点
        if (point.flg != -1) {//数据合法

            //更新计算缓存
            // this.calCache.proTimeValLong = point.timeLong;
            //获取下一条轨迹数据
            if (point.tirdFlg == true) {//该数据为疲劳驾驶后的第一条数据,直接使用上条数据的经纬度
                point.latitude = this.cache.routList[this.calCache.routIndex].latitude;
                point.longitude = this.cache.routList[this.calCache.routIndex].longitude;
                point.speed = -1;//速度为-1，表示要设置为0
                point.distance = 0;

            } else {
                this.getNextGpsRoutIndex(this.calCache.routIndex + 1, point);
            }
            
            //校验数据合法性
            if (point.latitude > 0 && point.longitude > 0) {
                

                delete point.flg;
                delete point.tirdFlg;
                this.formatGpsData(point);
                //匹配数据!
                this.cache.gpsList.push(point);

                //合法，继续获取下一个点
                this.getNextTimeGpsData();//遍历方法
            }else{
                // this.outDebug("获取最近一条非法的gps轨迹数据：" ,point);
                // this.outDebug("当前的索引为：" + this.calCache.routIndex);
                if(point.topSpeed > 0 && point.topSpeed > this.calCache.topSpeed){
                    this.calCache.topSpeed = point.topSpeed;
                }
            }

        }//数据不合法，说明时间到底了，那么不获取下一个时间点了
    } catch (error) {
        this.cache.errorMsg += "获取下一个时间的gps数据点失败：" + error;
    }
    
}

//格式化GPS数据，进行补全
InsertGps.formatGpsData = function(point){
    if(point.speed == -1){
        point.speed = 0;
    }
    point.plateNo = this.cache.statusData.plateNo;//车牌号
    point.plateColor =  this.cache.statusData.plateColor;//车牌颜色
    point.simNo = this.cache.statusData.simNo;//simNo
    point.realSpeed = point.speed;
    if(point.speed > 80){
        point.speed = (80 - Math.random()*10).toFixed(1);
    }
    point.recSpeed = point.speed;//速度
    point.direction = parseInt(Math.random() * 360);//方向
    point.altitude = this.cache.statusData.altitude;//海拔
    point.createDate = this.cache.statusData.createDate;
    this.cache.statusData.mileage += point.distance/1000;
    point.mileage = this.cache.statusData.mileage.toFixed(1);
    point.sendTime = point.time;
    point.distance = point.distance.toFixed(1);
}

//获取下一个时间值
InsertGps.getNextTimeValue = function(){
    let point = {
        flg:0,//标记，默认为0，表示数据点合法，为-1 表示不合法
        tirdFlg:false,//是否为疲劳驾驶后第一个点，如果是则标记为true，如果不是，则标记为false
        time:"",//时间描述值
        timeLong:0,//时间值，长整型
        latitude:0,//纬度
        longitude:0,//经度,
        speed:0,//速度
        topSpeed:0,//发生超速时的最高速
        routIndex:-1,
    };
    let next = this.calCache.proTimeValLong + this.config.onePointTime * 1000 ;//获取下一个时间点:上一时间点加上一个点的位移，加上偏移量
    // this.addDebug("初次获取的下一时间为：" + Utility.dateToString(new Date(next)));

    

    //校验疲劳驾驶时间，如果超出疲劳驾驶时间，则加上休息时间，然后数据点设置为当前点
    if(this.config.noTirdFlg ==  true){//允许校验,
        if(next - this.calCache.proTirdTimeValLong >= this.config.tirdValue * 1000){//当前时间超过疲劳驾驶时间
            next = this.calCache.proTirdTimeValLong + this.config.tirdValue * 1000 + this.config.restValue * 1000;
            this.addDebug("疲劳驾驶，需要进行休息，上一疲劳驾驶点为：" + Utility.dateToString(new Date(this.calCache.proTirdTimeValLong)) + 
                        " ， 新的疲劳驾驶点为：" + Utility.dateToString(new Date(next)));
            this.calCache.proTirdTimeValLong = next;
            point.tirdFlg = true;//标记当前时间点为疲劳驾驶后的第一个点，不需要进行新的经纬度获取，直接获取上个位置的经纬度
        }
    }

    //校验时间是否在2点到5点之间
    if(this.config.noDriveFlg ==  true){//允许校验
        let hour = new Date(next).getHours();
        if(hour >= this.config.noDriveStartHour && hour < this.config.noDriveEndHour){
            this.outDebug("当前时间在2点到5点之间，时间为：" + Utility.dateToString(new Date(next)));
            //在两点到5点之间
            let next2 =  new Date(next);
            next2 = next2.setHours(5,3,1,0);
            next = new Date(next2).getTime();
            point.tirdFlg = true;//因为要设置当前点的速度为0，所以借用tired变量进行设置
            this.calCache.proTirdTimeValLong = next;//更新疲劳驾驶时间点至5点之后
            this.outDebug("需要避开两点到五点之间的时间，新的下一时间为：" + Utility.dateToString(new Date(next)));
        }
    }

    //补全数据
    point.timeLong = next;
    point.time = Utility.dateToString(new Date(next));

    //更新计算缓存
    this.calCache.proTimeValLong = point.timeLong;

    // 校验当前时间是否小于结束时间
    if(this.calCache.endTimeValueLong - next < this.config.onePointTime * 1000){
        point.flg = -1 ;//当前时间距离终点太近，不合法
        point.msg += "";
    }

    return point;
}


//下一个GPS点的经纬度在轨迹中的索引
InsertGps.getNextGpsRoutIndex = function(index,point){
    if(index < this.cache.routList.length){
        let nowPoint = this.cache.routList[index];//轨迹中的点
        let proPoint = this.getLastPoint();//上一个获取到的点
        if(!DataUtil.isDefined(nowPoint) && !DataUtil.isDefined(proPoint)){//数据存在
            if(this.checkSpeed(nowPoint,proPoint,point) ==  true){
                this.calCache.routIndex ++ ;//更新获取的数据所在索引
                point.latitude = nowPoint.latitude;
                point.longitude = nowPoint.longitude;
                point.routIndex = this.calCache.routIndex;
                point.errmsg = "";
                this.getNextGpsRoutIndex(this.calCache.routIndex + 1,point);//遍历
            } else {
                point.routIndex = this.calCache.routIndex;
                let lastRoutIndex = this.getLastRoutIndex();
                if(lastRoutIndex == point.routIndex) {//最近的两个点都超速
                    // this.outDebug("当前点距离太远，lastRoutIndex= " + lastRoutIndex + "， routIndex = " + point.routIndex +"当前时间点:",point);
                    let newPoint = this.getNextTimeValue();//获取到下一个时间点
                    point.timeLong = newPoint.timeLong;
                    point.time = newPoint.time;
                    point.flg = newPoint.flg;
                    point.tirdFlg = newPoint.tirdFlg;
                    // this.outDebug("新时间点：" ,point);
                    if (point.flg != -1) {//时间点合法
                        //更新计算缓存
                        // this.calCache.proTimeValLong = point.timeLong;
                        //获取下一条轨迹数据
                        if (point.tirdFlg == true) {//该数据为疲劳驾驶后的第一条数据,直接使用上条数据的经纬度
                            point.latitude = this.cache.routList[this.calCache.routIndex].latitude;
                            point.longitude = this.cache.routList[this.calCache.routIndex].longitude;
                            point.speed = -1;//速度为-1，表示要设置为0
                            point.distance = 0;

                        } else {
                            this.getNextGpsRoutIndex(this.calCache.routIndex + 1, point);
                        }
                    }
                }
            }
        }
    }else{
        //结束
        point.routIndex = this.calCache.routIndex;
    }
}

//获取最近的轨迹索引s
InsertGps.getLastRoutIndex = function(){
    let routIndex = -1;
    if(this.cache.gpsList.length > 0){
        //之前有数据
        let index = this.cache.gpsList.length -1;
        let obj = this.cache.gpsList[index];
        if(!DataUtil.isDefined(obj)){
            routIndex = obj.routIndex;
        }
    }//无gps定位数据说明索引就是为-1 
    return routIndex;
}




//获取当前最后一个gps数据点，如果没有，则返回起点
InsertGps.getLastPoint = function(){
    if(this.cache.gpsList.length > 0){
        return this.cache.gpsList[this.cache.gpsList.length-1];
    }else{
        return this.cache.start;
    }
}


//校验两个点之间的速度，如果如果速度超过config.maxSpeed则返回false
InsertGps.checkSpeed = function(nowPoint,proPoint,point){
    let flg = false;
    let timeValue = Math.abs((proPoint.timeLong - point.timeLong)/1000);
    if(timeValue > 0){
        let distance = this.algorithm(nowPoint.longitude,nowPoint.latitude,proPoint.longitude,proPoint.latitude);
        
        let speed = distance * 3.6 /timeValue;
        if(speed> 0){

        }else{
            this.outDebug("speed = " + speed);
        }
        // this.outDebug("当前速度为："+ speed.toFixed(1) + "(km/h)");
        let maxSpeed = this.config.maxSpeed;
        if(this.cache.gpsList.length == 0){//如果是校验第一个点的速度，则速度降低10
            maxSpeed = maxSpeed - 10;
        }
        if(speed <= maxSpeed){
            flg = true;
            point.speed = speed.toFixed(1);
            point.distance = distance;
            // point.speedMsg = "速度为：" + speed.toFixed(1) +"(km/h) , 距离为：" + distance.toFixed(1) + " 米，时间差为：" + timeValue + "秒";
        }else{
            // point.errmsg = "速度为：" + speed.toFixed(1) +"(km/h) , 距离为：" + distance.toFixed(1) + " 米，时间差为：" + timeValue + "秒";
            point.topSpeed = speed;//保存最高速度
        }
    }
    return flg;
}


///////////////////////////////////////////////////////计算距离

//计算两点间的距离，单位为米
InsertGps.algorithm = function(longitude1,latitude1,longitude2,latitude2){
    let s = 0.0;
    // this.outDebug("开始进行两点间距离计算..." );
    try {
        // this.outDebug("开始进行两点间距离计算2..." );
        let lat1 = ins_m.rad(latitude1);//纬度
        // this.outDebug("lat1 = " + lat1);
        let lat2 = ins_m.rad(latitude2);
        // this.outDebug("lat2 = " + lat2);
        let a = lat1 - lat2;
        // this.outDebug("a = " + a);
        let b = this.rad(longitude1) - this.rad(longitude2);
        // this.outDebug(" b = " + b);
        s = 2*Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) + Math.cos(lat1)*Math.cos(lat2) * Math.pow(Math.sin(b/2),2)));
        // this.outDebug("s = " + s);
        s = s * 6378137.0;//弧长乘地球半径（半径为米)
        // this.outDebug("乘地球半径后的结果：" + s);
        s = Math.round(s * 10000.0)/10000.0;
    } catch (error) {
        // that.outDebug("计算两点间距离失败：" + error);
        s = -1;
    }
    return s;
}

//角度转弧度
InsertGps.rad = function(value){
    try {
        return (value * Math.PI / 180);
    } catch (error) {
        // this.outDebug("角度转弧度失败：" + error);
        return NaN;
    }
}


///////////////////////////////////////////////////////公用方法


//调试输出
InsertGps.outDebug = function(msg,data){
    if(this.debug == true){
        if(DataUtil.isDefined(data)){
            console.log(msg);
        }else{
            console.log(msg,data);
        }
    }
}

//输出信息
InsertGps.showInfo = function(msg){
    try {
        $("#"+ this.msgDivId).html(msg);
    } catch (error) {
        console.log("输出信息失败，信息：" + msg + "----原因：" + error);
    } 
}


//添加调试信息
InsertGps.addDebug = function(msg){
    this.cache.debugMsg.push(Utility.dateToString(new Date())+ "---->" + msg);
}

//获取警告和严重信息
InsertGps.getAllMsg = function(){
    return (this.cache.waringMsg + this.cache.errorMsg);
}